package com.wemew.securityapi.config.security.filter;

import com.wemew.common.enums.error.Error;
import com.wemew.common.exceptionhandler.GuliException;
import com.wemew.common.redis.UserRedis;
import com.wemew.common.utils.R;
import com.wemew.common.utils.ResponseUtil;
import com.wemew.securityapi.config.security.config.TokenManager;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jws;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.web.authentication.www.BasicAuthenticationFilter;
import org.springframework.util.StringUtils;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;

/**
 * 作者 CG
 * 时间 2021/1/13 21:06
 * 注释 权限拦截器
 */
public class TokenAuthFilter extends BasicAuthenticationFilter {

    private TokenManager tokenManager;
    private UserRedis userRedis;

    public TokenAuthFilter(AuthenticationManager authenticationManager, TokenManager tokenManager, UserRedis userRedis) {
        super(authenticationManager);
        this.tokenManager = tokenManager;
        this.userRedis = userRedis;
    }

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws IOException, ServletException {
        try {
//获取当前认证成功用户权限信息
            UsernamePasswordAuthenticationToken authRequest = getAuthentication(request);
            //判断如果有权限信息，放到权限上下文中
            if (authRequest != null) {
                SecurityContextHolder.getContext().setAuthentication(authRequest);
            }
        } catch (AuthenticationException e) {
            SecurityContextHolder.clearContext();
            ResponseUtil.out(response, new R(e.getCause()));
            return;
        }
        chain.doFilter(request, response);
    }

    private UsernamePasswordAuthenticationToken getAuthentication(HttpServletRequest request) {
        //从header获取token
        String token = request.getHeaders("Authorization").nextElement();
        if (token != null) {
            //从token获取用户名
            String userName = checkToken(token);
            //从redis获取对应权限列表
            List<String> permissionValueList = userRedis.getPermissions(userName);
            if (permissionValueList==null||permissionValueList.isEmpty()) {
                throw new UsernameNotFoundException("", new GuliException(Error.Error4007));
            }
            Collection<GrantedAuthority> authority = new ArrayList<>();
            for (String permissionValue : permissionValueList) {
                SimpleGrantedAuthority auth = new SimpleGrantedAuthority(permissionValue);
                authority.add(auth);
            }
            return new UsernamePasswordAuthenticationToken(userName, token, authority);
        }
        return null;
    }

    /**
     * 作者 CG
     * 时间 2021/1/14 15:04
     * 注释 检查token是否有效
     */
    public String checkToken(String token) {
        token = token.replaceAll("Bearer ", "");
        Jws<Claims> jwt = tokenManager.getJwt(token);
        tokenManager.ifExpiration(jwt);
        String code = tokenManager.getCode(jwt);
        String userName = tokenManager.getUserInfoFromToken(jwt);
        String tokenCode = userRedis.getTokenCode(userName);
        //检查服务器code是否过期
        if (StringUtils.isEmpty(token)||StringUtils.isEmpty(tokenCode)) {
            throw new UsernameNotFoundException("", new GuliException(Error.Error4008));
        }
        //检查账号是否在其他设备登录
        if (!tokenCode.equals(code)) {
            throw new UsernameNotFoundException("", new GuliException(Error.Error4009));
        }
        return userName;
    }

    @Override
    protected void onUnsuccessfulAuthentication(HttpServletRequest request, HttpServletResponse response, AuthenticationException failed) throws IOException {
        if (failed instanceof UsernameNotFoundException) {
            ResponseUtil.out(response, new R(failed.getCause()));
        } else {
            ResponseUtil.out(response, new R(failed));
        }
    }
}
